package de.velvettear.squirrel.util;

import java.util.Collection;
import java.util.Vector;

import org.jivesoftware.smack.AccountManager;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Presence.Mode;
import org.jivesoftware.smack.packet.RosterPacket.ItemStatus;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;

import android.app.AlertDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;
import de.velvettear.squirrel.kernel.Contact;
import de.velvettear.squirrel.kernel.Settings;
import de.velvettear.squirrel.kernel.Statics;

public class ChatHelper {

	private static XMPPTCPConnection connection = null;
	
	public static void checkSettings(Context context, Handler dialogHandler) {		
		String username = new Settings().getValueByName(Statics.SETTINGS_CHAT_USERNAME);
		String password = new Settings().getValueByName(Statics.SETTINGS_CHAT_PASSWORD);
				
		if (username.trim().length() == 0 && password.trim().length() == 0) {
			AlertDialog noSettings = DialogHelper.createConfirmationDialog(context,
												"Error",
												"Could not find any login settings.\nPlease make sure your client is configured correctly.",
												"Open settings", "Close",
												dialogHandler,
												Statics.COMMAND_SETTINGS_CHAT);
			noSettings.show();
		} else if (!isConnected()) {
			AlertDialog noSettings = DialogHelper.createConfirmationDialog(context,
					"Error",
					"Could not connect to the server.\nPlease make sure your client is connected to the internet.",
					"Open settings", "Close",
					dialogHandler,
					Statics.COMMAND_INTENT_SETTINGS);
			noSettings.show();
		} else if (!isAuthenticated()) {
			AlertDialog noSettings = DialogHelper.createConfirmationDialog(context,
					"Error",
					"Credentials are not recognized by the server.\nWould you like to create an account with these credentials?",
					"Yes", "No",
					dialogHandler,
					Statics.COMMAND_CHAT_ACCOUNTCREATION);
			noSettings.show();			
		} else {
			dialogHandler.sendMessage(MessageHelper.createMessage(Statics.HANDLER_CHAT, Statics.COMMAND_CHAT_CONTACTLIST, null));
		}
		
	}
	
	public static void connect(Handler dialogHandler) {
		final Handler handler = dialogHandler;
		
		AsyncTask<Void, Void, XMPPTCPConnection> connectionTask = new AsyncTask<Void, Void, XMPPTCPConnection>() {

			@Override
			protected XMPPTCPConnection doInBackground(Void... params) {
				String host = new Settings().getValueByName(Statics.SETTINGS_CHAT_HOST);
				String port = new Settings().getValueByName(Statics.SETTINGS_CHAT_PORT);
				
				// Oeffne neue Verbindung zu Server
				if (connection == null) {
					ConnectionConfiguration config = new ConnectionConfiguration(host, Integer.parseInt(port), new Settings().getValueByName(Statics.SETTINGS_CHAT_SERVICE));
					config.setSecurityMode(SecurityMode.disabled);
					connection = new XMPPTCPConnection(config);
				}		
							
				try {
					// Verbinde mit Server
					if (!connection.isConnected()) {
						connection.connect();				
					}
									
					// Einloggen, falls noetig
					if (!connection.isAuthenticated()) {
						String username = new Settings().getValueByName(Statics.SETTINGS_CHAT_USERNAME);
						String password = new Settings().getValueByName(Statics.SETTINGS_CHAT_PASSWORD); 
										
						connection.login(username, password);
					}
					
					if (isConnected() && isAuthenticated()) {
						Roster roster = connection.getRoster();
						roster.reload();
						
						roster.setSubscriptionMode(SubscriptionMode.accept_all);
						
						addContactListListener(handler);
//						addSubscriptionListener(handler);
					}
					
				} catch (Exception e) {
					if (e.getMessage() != null) {
						Log.e("connect()", e.getMessage());
					}
				}
				return connection;
			}
			
			@Override
			protected void onPostExecute(XMPPTCPConnection ret) {		
				if (isConnected() && isAuthenticated()) {
					handler.sendMessage(MessageHelper.createMessage(Statics.HANDLER_CHAT, Statics.COMMAND_CHAT_LOGIN, Statics.CHAT_LOGIN_OK));
				} else {
					handler.sendMessage(MessageHelper.createMessage(Statics.HANDLER_CHAT, Statics.COMMAND_CHAT_LOGIN, Statics.CHAT_LOGIN_FAILED));
				}
			}
			
		};
		
		connectionTask.execute();	
	}
			
	public static void disconnect() {
		if (connection != null) {
			setPresence(Statics.CHAT_TYPE_LOGGEDOFF, null, null);
			try {
				connection.disconnect();
				connection = null;
			} catch (Exception e) {
				if (e.getMessage() != null) {
					Log.e("disconnect()", e.getMessage());
				}
			}			
		}
	}
	
	private static boolean isConnected() {
		boolean isConnected = false;
		
		if (connection != null) {
			if (connection.isConnected()) {
				isConnected = true;
			}
		}
		
		return isConnected;
	}
	
	private static boolean isAuthenticated() {
		boolean isAuthenticated = false;
		
		if (connection != null) {
			if (connection.isAuthenticated()) {
				isAuthenticated = true;
			}
		}
		
		return isAuthenticated;
	}
	
	public static void unregisterAccount() {
		unregisterAccount(null);
	}
	
	public static void unregisterAccount(Handler guiHandler) {
		final Handler handler = guiHandler;
		
		AsyncTask<Void, Void, Boolean> unregisterTask = new AsyncTask<Void, Void, Boolean>() {

			@Override
			protected Boolean doInBackground(Void... params) {
				boolean accountUnregistered = false;
				
				String username = new Settings().getValueByName(Statics.SETTINGS_CHAT_USERNAME);
				String host = new Settings().getValueByName(Statics.SETTINGS_CHAT_HOST);
				String port = new Settings().getValueByName(Statics.SETTINGS_CHAT_PORT);
				
				// Oeffne neue Verbindung zu Server
				if (connection == null) {
					ConnectionConfiguration config = new ConnectionConfiguration(host, Integer.parseInt(port), new Settings().getValueByName(Statics.SETTINGS_CHAT_SERVICE));
					config.setSecurityMode(SecurityMode.disabled);
					connection = new XMPPTCPConnection(config);
				}		
							
				try {
					// Verbinde mit Server
					if (!connection.isConnected()) {
						connection.connect();				
					}
					
					// Benutzer loeschen					
					AccountManager manager = AccountManager.getInstance(connection);
					if (manager.supportsAccountCreation()) {
						manager.deleteAccount();
						accountUnregistered = true;
					}		
				} catch (XMPPErrorException xmpp) {
					if (xmpp.getMessage() != null) {
						Log.e("unregisterAccount()", xmpp.getMessage());
						if (xmpp.getMessage().equals(Statics.EXCEPTION_CHAT_NOTALLOWED)) {
							accountUnregistered = true;
						}
					}
				} catch (Exception e) {
					if (e.getMessage() != null) {
						Log.e("unregisterAccount()", e.getMessage());
					}
				}
				
				try {
					MySQLHelper.deleteJabberUser(username);
				} catch (Exception e) {
					if (e.getMessage() != null) {
						Log.e("unregisterAccount()", e.getMessage());
					}
				}
				
				return accountUnregistered;
			}
			
			@Override
			protected void onPostExecute(Boolean ret) {		
				if (handler != null) {
					if (ret) {
						handler.sendMessage(MessageHelper.createMessage(Statics.HANDLER_CHAT, Statics.COMMAND_CHAT_ACCOUNTDELETION, Statics.STATUS_SUCESS));
					} else {
						handler.sendMessage(MessageHelper.createMessage(Statics.HANDLER_CHAT, Statics.COMMAND_CHAT_ACCOUNTDELETION, Statics.STATUS_ERROR));
					}
				}	
			}
			
		};
		
		unregisterTask.execute();
	}
	
	public static void registerAccount() {
		registerAccount(null);
	}
	
	public static void registerAccount(Handler guiHandler) {
		final Handler handler = guiHandler;
		
		AsyncTask<Void, Void, Boolean> registerTask = new AsyncTask<Void, Void, Boolean>() {

			@Override
			protected Boolean doInBackground(Void... params) {
				boolean accountRegistered = false;
				
				String username = new Settings().getValueByName(Statics.SETTINGS_CHAT_USERNAME);
				String alias = new Settings().getValueByName(Statics.SETTINGS_CHAT_ALIAS);
				String password = new Settings().getValueByName(Statics.SETTINGS_CHAT_PASSWORD);
				String host = new Settings().getValueByName(Statics.SETTINGS_CHAT_HOST);
				String port = new Settings().getValueByName(Statics.SETTINGS_CHAT_PORT);
				
				// Oeffne neue Verbindung zu Server
				if (connection == null) {
					ConnectionConfiguration config = new ConnectionConfiguration(host, Integer.parseInt(port), new Settings().getValueByName(Statics.SETTINGS_CHAT_SERVICE));
					config.setSecurityMode(SecurityMode.disabled);
					connection = new XMPPTCPConnection(config);
				}		
							
				try {
					// Verbinde mit Server
					if (!connection.isConnected()) {
						connection.connect();				
					}
					
					// Benutzer anlegen					
					AccountManager manager = AccountManager.getInstance(connection);
					if (manager.supportsAccountCreation()) {
						manager.createAccount(username, password);
						accountRegistered = true;
					}		
				} catch (XMPPErrorException xmpp) {
					if (xmpp.getMessage() != null) {
						Log.e("registerAccount()", xmpp.getMessage());
						if (xmpp.getMessage().equals(Statics.EXCEPTION_CHAT_CONFLICT)) {
							accountRegistered = true;
						}
					}
				} catch (Exception e) {
					if (e.getMessage() != null) {
						Log.e("registerAccount()", e.getMessage());
					}
				}
				
				try {
					MySQLHelper.createOrUpdateJabberUser(username, alias, password);
				} catch (Exception e) {
					if (e.getMessage() != null) {
						Log.e("registerAccount()", e.getMessage());
					}
				}
				
				return accountRegistered;
			}
			
			@Override
			protected void onPostExecute(Boolean ret) {		
				if (handler != null) {
					if (ret) {
						handler.sendMessage(MessageHelper.createMessage(Statics.HANDLER_CHAT, Statics.COMMAND_CHAT_ACCOUNTCREATION, Statics.STATUS_SUCESS));
					} else {
						handler.sendMessage(MessageHelper.createMessage(Statics.HANDLER_CHAT, Statics.COMMAND_CHAT_ACCOUNTCREATION, Statics.STATUS_ERROR));
					}
				}				
			}
			
		};
		
		registerTask.execute();
	}
		
	public static void setPresence(String mode, String status) {
		setPresence(Statics.CHAT_TYPE_LOGGEDIN, mode, status);
	}
	
	public static void setPresence(String type, String mode, String status) {
		final String presenceType = type;
		final String presenceMode = mode;
		final String statusText = status;
		
		AsyncTask<Void, Void, Void> presenceTask = new AsyncTask<Void, Void, Void>() {

			@Override
			protected Void doInBackground(Void... params) {				
				if (isConnected() && isAuthenticated()) {
					try {
						Presence presence = null;
						if (presenceType.equals(Statics.CHAT_TYPE_LOGGEDOFF)) {
							presence = new Presence(Presence.Type.unavailable);
						} else {
							presence = new Presence(Presence.Type.available);
						}						 
						if (presenceMode != null) {
							presence.setMode(stringToMode(presenceMode));
						}
						if (statusText != null) {
							presence.setStatus(statusText);
						}
						connection.sendPacket(presence);
					} catch (Exception e) {
						if (e.getMessage() != null) {
							Log.e("setPresence()", e.getMessage());
						}
					}					
				}
				
				return null;
			}
			
			@Override
			protected void onPostExecute(Void ret) {		
				
			}
			
		};		
		
		presenceTask.execute();
	}
	
	public static void getContactList(Handler guiHandler) {
		final Handler handler = guiHandler;
		
		AsyncTask<Void, Void, Vector<Contact>> contactsTask = new AsyncTask<Void, Void, Vector<Contact>>() {

			@Override
			protected Vector<Contact> doInBackground(Void... params) {
				Vector<Contact> contactList = new Vector<Contact>();
				try {				
					Roster roster = connection.getRoster();
					roster.reload();
											
					contactList = MySQLHelper.getJabberUsers();
					for (Contact contact : contactList) {
						if (!roster.contains(contact.m_sName)) {
							roster.createEntry(contact.m_sName, contact.m_sAlias, null);							
						}
					}													
					roster.reload();
					
					Collection<RosterEntry> entries = roster.getEntries();				
					for (RosterEntry entry : entries) {
						if (entry.getUser().equals(new Settings().getValueByName(Statics.SETTINGS_CHAT_USERNAME))) {
							continue;
						}
						boolean deleteEntry = true;
						for (Contact contact : contactList) {
							if (entry.getUser().toLowerCase().equals(contact.m_sName.toLowerCase())) {
								deleteEntry = false;
							}
						}
						if (deleteEntry) {
							roster.removeEntry(entry);
						}
					}
					roster.reload();
					
					entries = roster.getEntries();	
					for (RosterEntry entry : entries) {
						checkSubscription(entry);
						for (Contact contact : contactList) {
							if (contact.m_sName.equals(entry.getName())) {
								contact.m_sStatus = entry.getStatus().toString();
							}
						}
					}					
				} catch (Exception e) {
					if (e.getMessage() != null) {
						Log.e("getContactList()", e.getMessage());
					}
				}
				
				return contactList;
			}
			
			@Override
			protected void onPostExecute(Vector<Contact> ret) {		
				handler.sendMessage(MessageHelper.createContactListMessage(ret));
			}
			
		};
		
		contactsTask.execute();
	}
		
//	public static void addSubscriptionListener(Handler subscriptionHandler) {
//		connection.addPacketListener(new PacketListener() {			
//			@Override
//			public void processPacket(Packet packet) throws NotConnectedException {
//				if (packet instanceof Presence) {
//					Presence pres = (Presence) packet;
//					if (Presence.Type.subscribe.equals(pres.getType())) {
//						Presence presence = new Presence(Presence.Type.subscribed);
//						presence.setTo(packet.getFrom());
//						connection.sendPacket(presence);
//					}
//				}
//			}
//		}, new PacketFilter() {			
//			public boolean accept(Packet packet) {
//				if (packet instanceof Presence) {
//					Presence pres = (Presence) packet;
//					return ((Presence.Type.subscribe.equals(pres.getType())) 
//						|| (Presence.Type.unsubscribe.equals(pres.getType()))
//						|| (Presence.Type.subscribed.equals(pres.getType()))
//						|| (Presence.Type.unsubscribed.equals(pres.getType())));
//				}
//				return false;
//			}});
//	}
	
	public static void addContactListListener(Handler presenceHandler) {
		final Handler handler = presenceHandler;
		
		Roster roster = connection.getRoster();
		roster.addRosterListener(new RosterListener() {
			
			@Override
			public void presenceChanged(Presence pres) {
				String sender = pres.getFrom();
				String status = pres.getStatus();
				String mode = Statics.CHAT_MODE_ONLINE;
				if (pres.getMode() != null) {
					mode = modeToString(pres.getMode());
				}
				String type = Statics.CHAT_TYPE_LOGGEDOFF;
				if (pres.getType().equals(Presence.Type.available)) {
					type = Statics.CHAT_TYPE_LOGGEDIN;
				}
				handler.sendMessage(MessageHelper.createPresenceMessage(sender, status, mode, type));
			}
			
			@Override
			public void entriesUpdated(Collection<String> entries) {
				
			}
			
			@Override
			public void entriesDeleted(Collection<String> entries) {
				
			}
			
			@Override
			public void entriesAdded(Collection<String> entries) {
				
			}
		});
		
		try {
			roster.reload();
		} catch (Exception e) {
			if (e.getMessage() != null) {
				Log.e("addContactListListener()", e.getMessage());
			}
		}
	}
	
	public static void sendMessage(String receiver, String text) {
		final String rec = receiver + "@" + new Settings().getValueByName(Statics.SETTINGS_CHAT_SERVICE);
		final String txt = text;
		
		AsyncTask<Void, Void, Void> sendTask = new AsyncTask<Void, Void, Void>() {

			@Override
			protected Void doInBackground(Void... params) {				
				if (isConnected() && isAuthenticated()) {
					try {
						Message msg = new Message(rec);
						msg.setBody(txt);
						connection.sendPacket(msg);
					} catch (Exception e) {
						if (e.getMessage() != null) {
							Log.e("sendMessage()", e.getMessage());
						}
					}					
				}
				
				return null;
			}
			
			@Override
			protected void onPostExecute(Void ret) {		
				
			}
			
		};		
		
		sendTask.execute();
	}
	
	private static void checkSubscription(RosterEntry entry) {
		try {
//			ItemType type = entry.getType();
			ItemStatus status = entry.getStatus();
//			
			if (status.equals(ItemStatus.subscribe)) {
				Presence presence = new Presence(Presence.Type.subscribed);
				String user = entry.getUser() + "@" + new Settings().getValueByName(Statics.SETTINGS_CHAT_SERVICE);
				presence.setTo(user);
				connection.sendPacket(presence);
			}
//			
//			if (type.equals(ItemType.from)) {
//				Presence subscribed = new Presence(Presence.Type.subscribed);
//				subscribed.setTo(entry.getName());
//				connection.sendPacket(subscribed);
//				
//				Presence subscribe = new Presence(Presence.Type.subscribe);
//				subscribe.setTo(entry.getName());
//				connection.sendPacket(subscribe);
//			} else if (type.equals(ItemType.to)) {
//				
//			} else if (type.equals(ItemType.none)) {
//				Presence subscribe = new Presence(Presence.Type.subscribe);
//				subscribe.setTo(entry.getUser());
//				connection.sendPacket(subscribe);
//			} 
		} catch (Exception e) {
			if (e.getMessage() != null) {
				Log.e("checkSubscription()", e.getMessage());
			}
		}		
	}
	
	public static String modeToString(Mode mode) {
		String modeString = "";
		
		if (mode.equals(Mode.available)) {
			modeString = Statics.CHAT_MODE_ONLINE;
		} else if (mode.equals(Mode.away)) {
			modeString = Statics.CHAT_MODE_BUSY;
		} else if (mode.equals(Mode.xa)) {
			modeString = Statics.CHAT_MODE_AWAY;
		} else if (mode.equals(Mode.dnd)) {
			modeString = Statics.CHAT_MODE_DND;
		}
		
		return modeString;	
	}
	
	public static Mode stringToMode(String modeString) {
		Mode mode = null;
		
		if (modeString.equals(Statics.CHAT_MODE_ONLINE)) {
			mode = Mode.available;
		} else if (modeString.equals(Statics.CHAT_MODE_BUSY)) {
			mode = Mode.away;
		} else if (modeString.equals(Statics.CHAT_MODE_AWAY)) {
			mode = Mode.xa;
		} else if (modeString.equals(Statics.CHAT_MODE_DND)) {
			mode = Mode.dnd;
		}
		
		return mode;
	}
	
}
